﻿using System;
using System.Linq;
using Stimulus360.Models.Data;
using System.Collections.Generic;
using Stimulus360.Models.Data.CustomTypes;

namespace Stimulus360.Repositories
{

    /// <summary>
    ///     This class is used for CRUD operations acorss the project entity
    /// </summary>
    public partial class DataRepository 
    {

        #region IProjectRepository Members

        /// <summary>
        ///     Method to retrieve the list of new projects in the system
        /// </summary>
        /// <returns>
        ///     List of new projects in the system
        /// </returns>
        public IQueryable<Project> GetNewProjects()
        {
            DateTime viewDate = DateTime.Now.Subtract(new TimeSpan(CalendarYear.ThirtyDays , 0, 0, 0));
            return GetProjectsCreatedOnAfter(viewDate);
        }



        /// <summary>
        ///     Method to get all projects in a given category in descending order of date submitted.
        /// </summary>
        /// <param name="category">
        ///     Category title of the Category
        /// </param>
        /// <returns>
        ///     All projects in the given category in descending order of date submitted.
        /// </returns>
        public IQueryable<Project> FindProjectsInCategory(string category)
        {
            return from project in _dataContext.Projects
                   where project.InvestmentCategory.Category == category
                   orderby project.DateCreated descending
                   select project;
        }

        /// <summary>
        ///     Method to retrieve list of projects satisfying the search criteria
        /// </summary>
        /// <param name="searchText">
        ///     The search criteria
        /// </param>
        /// <returns>
        ///     The list of projects satisfying the search criteria
        /// </returns>
        public IQueryable<Project> FindProjectsBySearchString(string searchText)
        {
            return from project in _dataContext.Projects
                   where (project.Title.ToLower().Contains(searchText.ToLower()) || (project.InvestmentCategory.Category.ToLower() == searchText.ToLower()))
                   orderby project.DateCreated descending
                   select project;
        }

        /// <summary>
        ///     Method to get all projects created on or after the given date
        ///     in descending order of date created.
        /// </summary>
        /// <param name="date">
        ///     The date to be considered.
        /// </param>
        /// <returns>
        ///     All projects created on or after the given date
        ///     in descending order of date created.
        /// </returns>
        private IQueryable<Project> GetProjectsCreatedOnAfter(DateTime date)
        {
            return from project in _dataContext.Projects
                   where project.DateCreated >= date && project.DateCreated <=DateTime.Now 
                   orderby project.DateCreated descending
                   select project;
        }

        /// <summary>
        ///     Method to get the top N Projects ordered by Average rating
        /// </summary>
        /// <param name="topN">
        ///     Number of projects to be returned
        /// </param>
        /// <returns>
        ///     Top "N" projects ordered by Average rating
        /// </returns>
        public IQueryable<Project> GetTopNProjects(int topN)
        {
            return (from project in _dataContext.Projects
                    orderby project.AverageRating descending
                    select project).Take(topN);
        }

        /// <summary>
        ///     Method to get all projects depending of where Volunteers are required or not
        /// </summary>
        /// <param name="required">
        ///     Bool indicating if projects are required or not.
        /// </param>
        /// <returns>
        ///     All projects depending of where Volunteers are required or not
        /// </returns>
        public IQueryable<Project> FindProjectsByVolunteerStatus(bool required)
        {
            return from project in _dataContext.Projects
                   where project.VolunteersRequired == required
                   orderby project.DateCreated descending
                   select project;
        }

        /// <summary>
        ///     Method to get all projects of the given status ordered by date submitted
        /// </summary>
        /// <param name="status">
        ///     The Status to be considered
        /// </param>
        /// <returns>
        ///    All projects of the given status ordered by date submitted
        /// </returns>
        public IQueryable<Project> FindProjectsByStatus(ProjectStatus status)
        {
            return from project in _dataContext.Projects
                   where project.Status == (byte)status
                   orderby project.DateCreated descending
                   select project;
        }

        /// <summary>
        ///     Method to return the project for the given project ID
        /// </summary>
        /// <param name="projectId">
        ///     The project ID for which project has to be returned.
        /// </param>
        /// <returns>
        ///     Project for the given project ID
        /// </returns>
        public Project FindProjectById(int projectId)
        {
            return _dataContext.Projects.FirstOrDefault(i => i.ProjectId == projectId);
        }

        /// <summary>
        ///     Method to get all feedback for the given project ordered by date submitted
        /// </summary>
        /// <param name="projectId">
        ///     Project ID of the project for which feedback is to be retrieved
        /// </param>
        /// <returns>
        ///     All feedback for the given project ordered by date submitted
        /// </returns>
        public IQueryable<ProjectRating> GetProjectFeedback(int projectId)
        {
            return from feedback in _dataContext.ProjectRatings
                   where feedback.ProjectId == projectId
                   orderby feedback.DateSubmitted descending
                   select feedback;
        }

        /// <summary>
        ///     This method is used to get the User's feedback for a specific Project
        /// </summary>
        /// <param name="projectId">
        ///     ProjectId for which feedback is to be retrieved.
        /// </param>
        /// <param name="userName">
        ///     User whose feedback is to be retrieved
        /// </param>
        /// <returns>
        ///     User Feedback for the given project.
        /// </returns>
        public ProjectRating GetProjectFeedback(int projectId, string userName)
        {
            return _dataContext.ProjectRatings.SingleOrDefault(r => ((r.UserName == userName) && r.ProjectId == projectId));
        }

        /// <summary>
        ///     Method to retrieve list of volunteers for the given project
        /// </summary>
        /// <param name="projectId">
        ///     Project ID for which list of volunteers is to be retrieved
        /// </param>
        /// <returns>
        ///     List of volunteers for the given project
        /// </returns>
        public IQueryable<Volunteer> GetProjectVolunteers(int projectId)
        {
            return from volunteer in _dataContext.Volunteers
                   where volunteer.ProjectId == projectId
                   orderby volunteer.DateVolunteered descending
                   select volunteer;
        }

        /// <summary>
        ///     Method to updated an edited project to the database
        /// </summary>
        public void UpdateProject()
        {
            _dataContext.SubmitChanges();
        }

        /// <summary>
        ///     Method to get the users favorite projects
        /// </summary>
        /// <param name="userName">
        ///     User whose favorite project list is to be retrieved
        /// </param>
        /// <returns>
        ///     The list of users favorite projects.
        /// </returns>
        public IQueryable<Project> GetFavoriteProjects(string userName)
        {
            //Since we want a IQuerable we cannot extract rating.Project from this query. To get
            // an IQueryable the query must exist on the entity itself
            var favoriteIds = from rating in _dataContext.ProjectRatings
                              where rating.UserName == userName && rating.Favorite == true
                              select rating.ProjectId;

            return from favProj in _dataContext.Projects
                   where favoriteIds.Contains(favProj.ProjectId)
                   select favProj;
        }

        /// <summary>
        ///     Method to get a list of recommended projects based on the users interests
        /// </summary>
        /// <param name="userName">
        ///     User for whom recommendations have to be made
        /// </param>
        /// <returns>
        ///     List of recommended projects.
        /// </returns>
        public IQueryable<Project> GetRecommendedProjects(string userName)
        {
            //All projects of User's interest created over last 30 days which the user has not rated.

            DateTime viewDate = DateTime.Now.Subtract(new TimeSpan(CalendarYear.ThirtyDays , 0, 0, 0));

            var latestProjects = GetProjectsCreatedOnAfter(viewDate);
            var userRatedProjects = from ratings in _dataContext.ProjectRatings
                                    where ratings.UserName == userName
                                    select ratings.Project ;
            var latestNonRatedProjects = latestProjects.Except(userRatedProjects);

            var userInterestIds = from userInterest in _dataContext.UserInterests
                                  where userInterest.UserName == userName
                                  select userInterest.CategoryId;
            return from project in latestNonRatedProjects
                   where (userInterestIds.Contains(project.InvestmentCategory.CategoryId))
                   orderby project.DateCreated
                   select project;

        }

        /// <summary>
        ///     Method to get a list of  projects for which the user has volunteered
        /// </summary>
        /// <param name="userName">
        ///     User under consideration
        /// </param>
        /// <returns>
        ///     List of volunteered projects.
        /// </returns>
        public IQueryable<Project> GetVolunteeredProjects(string userName)
        {
            //Since we want a IQuerable we cannot extract volunteer.Project from this query. To get
            // an IQueryable the query must exist on the entity itself
            var volunteerProjIds = from volunteer in _dataContext.Volunteers
                                   where volunteer.UserName == userName
                                   select volunteer.ProjectId;
            return from proj in _dataContext.Projects
                   where volunteerProjIds.Contains(proj.ProjectId)
                   orderby proj.DateCreated
                   select proj;
        }
        #endregion

        /// <summary>
        ///     This method is used to create a new project in the DB
        /// </summary>
        /// <param name="project">
        ///     The project to be created
        /// </param>
        public void CreateProject(Project project)
        {
            _dataContext.Projects.InsertOnSubmit(project);
            UpdateProject();
        }
    }
}
